﻿using System;
using System.Web.Hosting;
using System.Net;
using System.Net.Sockets;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
using System.Globalization;

//自定义命名空间
using DreamCube.Foundation.Basic.Win32API.API;

namespace WebServer.Service.WebHost
{
    [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust"), PermissionSet(SecurityAction.LinkDemand, Name = "Everything")]
    public class Server : MarshalByRefObject
    {
        #region "私有字段"

        private ApplicationManager _appManager;
        private bool _disableDirectoryListing;
        private Host _host;
        private object _lockObject;
        private WaitCallback _onSocketAccept;
        private WaitCallback _onStart;
        private string _physicalPath;
        private int _port;
        private IntPtr _processToken;
        private string _processUser;
        private bool _requireAuthentication;
        private bool _shutdownInProgress;
        private Socket _socketIpv4;
        private Socket _socketIpv6;
        private string _virtualPath;
        private const int SecurityImpersonation = 2;
        private const int TOKEN_ALL_ACCESS = 0xf01ff;
        private const int TOKEN_EXECUTE = 0x20000;
        private const int TOKEN_IMPERSONATE = 4;
        private const int TOKEN_READ = 0x20008;

        #endregion

        #region "属性"

        /// <summary>
        /// 网站的物理路径
        /// </summary>
        public String PhysicalPath
        {
            get { return this._physicalPath; }
        }

        /// <summary>
        /// 网站端口号
        /// </summary>
        public Int32 Port
        {
            get { return this._port; }
        }

        /// <summary>
        /// 网站的虚拟目录
        /// </summary>
        public String VirtualPath
        {
            get { return this._virtualPath; }
        }

        public String RootUrl
        {
            get
            {
                if (this._port != 80)
                {
                    return ("http://localhost:" + this._port + this._virtualPath);
                }
                return ("http://localhost" + this._virtualPath);
            }
        }

        #endregion

        #region "构造方法"

        public Server(int port, string virtualPath, string physicalPath)
            : this(port, virtualPath, physicalPath, false, false)
        {
        }

        public Server(int port, string virtualPath, string physicalPath, bool requireAuthentication)
            : this(port, virtualPath, physicalPath, requireAuthentication, false)
        {
        }

        public Server(int port, string virtualPath, string physicalPath, bool requireAuthentication, bool disableDirectoryListing)
        {
            this._lockObject = new object();
            this._port = port;
            this._virtualPath = virtualPath;
            this._physicalPath = physicalPath.EndsWith(@"\", StringComparison.Ordinal) ? physicalPath : (physicalPath + @"\");
            this._requireAuthentication = requireAuthentication;
            this._disableDirectoryListing = disableDirectoryListing;
            this._onSocketAccept = new WaitCallback(this.OnSocketAccept);
            this._onStart = new WaitCallback(this.OnStart);
            this._appManager = ApplicationManager.GetApplicationManager();
            this.ObtainProcessToken();
        }

        #endregion

        #region "公共方法"

        public IntPtr GetProcessToken()
        {
            return this._processToken;
        }

        public string GetProcessUser()
        {
            return this._processUser;
        }

        internal void HostStopped()
        {
            this._host = null;
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }

        public void Start()
        {
            bool flag = false;
            flag = Socket.OSSupportsIPv4;
            if (Socket.OSSupportsIPv6)
            {
                try
                {
                    this._socketIpv6 = this.CreateSocketBindAndListen(AddressFamily.InterNetworkV6, IPAddress.IPv6Loopback, this._port);
                }
                catch (SocketException exception)
                {
                    if ((exception.SocketErrorCode == SocketError.AddressAlreadyInUse) || !flag)
                    {
                        throw;
                    }
                }
            }
            if (flag)
            {
                try
                {
                    HttpListener listener = new HttpListener();
                    this._socketIpv4 = this.CreateSocketBindAndListen(AddressFamily.InterNetwork,
                                                                        IPAddress.Parse("192.168.1.107"), this._port);
                    //this._socketIpv4 = this.CreateSocketBindAndListen(AddressFamily.InterNetwork, IPAddress.Loopback, this._port);
                }
                catch (SocketException)
                {
                    if (this._socketIpv6 == null)
                    {
                        throw;
                    }
                }
            }
            if (this._socketIpv6 != null)
            {
                ThreadPool.QueueUserWorkItem(this._onStart, this._socketIpv6);
            }
            if (this._socketIpv4 != null)
            {
                ThreadPool.QueueUserWorkItem(this._onStart, this._socketIpv4);
            }
        }

        public void Stop()
        {
            this._shutdownInProgress = true;
            try
            {
                if (this._socketIpv4 != null)
                {
                    this._socketIpv4.Close();
                }
                if (this._socketIpv6 != null)
                {
                    this._socketIpv6.Close();
                }
            }
            catch
            {
            }
            finally
            {
                this._socketIpv4 = null;
                this._socketIpv6 = null;
            }
            try
            {
                if (this._host != null)
                {
                    this._host.Shutdown();
                }
                while (this._host != null)
                {
                    Thread.Sleep(100);
                }
            }
            catch
            {
            }
            finally
            {
                this._host = null;
            }
        }

        #endregion

        #region "私有方法"

        private void ObtainProcessToken()
        {
            if (ADVAPI32.ImpersonateSelf(2))
            {
                ADVAPI32.OpenThreadToken(kernel32.GetCurrentThread(), 0xf01ff, true, ref this._processToken);
                ADVAPI32.RevertToSelf();
                this._processUser = WindowsIdentity.GetCurrent().Name;
            }
        }

        private void OnSocketAccept(object acceptedSocket)
        {
            if (!this._shutdownInProgress)
            {
                Connection conn = new Connection(this, (Socket)acceptedSocket);
                if (conn.WaitForRequestBytes() == 0)
                {
                    conn.WriteErrorAndClose(400);
                }
                else
                {
                    Host host = this.GetHost();
                    if (host == null)
                    {
                        conn.WriteErrorAndClose(500);
                    }
                    else
                    {
                        host.ProcessRequest(conn);
                    }
                }
            }
        }

        private void OnStart(object listeningSocket)
        {
            while (!this._shutdownInProgress)
            {
                try
                {
                    if (listeningSocket != null)
                    {
                        Socket state = ((Socket)listeningSocket).Accept();
                        ThreadPool.QueueUserWorkItem(this._onSocketAccept, state);
                    }
                    continue;
                }
                catch
                {
                    Thread.Sleep(100);
                    continue;
                }
            }
        }

        /// <summary>
        /// 获取当前服务器的程序域
        /// </summary>
        /// <returns></returns>
        private Host GetHost()
        {
            if (this._shutdownInProgress)
            {
                return null;
            }
            Host host = this._host;
            if (host == null)
            {
                lock (this._lockObject)
                {
                    host = this._host;
                    if (host == null)
                    {
                        string appId = (this._virtualPath + this._physicalPath).ToLowerInvariant().GetHashCode().ToString("x", CultureInfo.InvariantCulture);
                        try
                        {
                            this._host = (Host)this._appManager.CreateObject(appId, typeof(Host), this._virtualPath, this._physicalPath, false);
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                        this._host.Configure(this, this._port, this._virtualPath, this._physicalPath, this._requireAuthentication, this._disableDirectoryListing);
                        host = this._host;
                    }
                }
            }
            return host;
        }

        /// <summary>
        /// 创建一个socket并启动监听
        /// </summary>
        /// <param name="family"></param>
        /// <param name="ipAddress"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        private Socket CreateSocketBindAndListen(AddressFamily family, IPAddress ipAddress, Int32 port, Int32 backlog = 1000000)
        {
            Socket socket = new Socket(family, SocketType.Stream, ProtocolType.Tcp)
            {
                ExclusiveAddressUse = false
            };
            try
            {
                socket.Bind(new IPEndPoint(ipAddress, port));
            }
            catch
            {
                socket.Close();
                socket = null;
                throw;
            }
            socket.Listen(backlog);
            return socket;
        }

        #endregion
    }
}
